package com.func.infrastructure.persistent.repository;

import cn.bugstack.middleware.db.router.strategy.IDBRouterStrategy;
import com.func.domain.activity.event.ActivitySkuStockZeroMessageEvent;
import com.func.domain.activity.model.aggregate.CreatePartakeOrderAggregate;
import com.func.domain.activity.model.aggregate.CreateQuotaOrderAggregate;
import com.func.domain.activity.model.entity.*;
import com.func.domain.activity.model.valobj.ActivitySkuStockKeyVO;
import com.func.domain.activity.repository.IActivityRepository;
import com.func.infrastructure.event.EventPublisher;
import com.func.infrastructure.persistent.dao.activity.*;
import com.func.infrastructure.persistent.dao.credit.IUserCreditAccountDao;
import com.func.infrastructure.persistent.po.activity.*;
import com.func.infrastructure.persistent.po.credit.UserCreditAccount;
import com.func.infrastructure.persistent.redis.IRedisService;
import com.func.types.common.Constants;
import com.func.types.enums.ResponseCode;
import com.func.types.exception.AppException;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RBlockingQueue;
import org.redisson.api.RDelayedQueue;
import org.redisson.api.RLock;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.support.TransactionTemplate;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * <p>
 * ActivityRepository
 * </p>
 *
 * @author Simon
 * @version 2025.03.11.1.0.0
 * @description ActivityRepository
 * @since 2025-03-11
 */

@Slf4j
@Repository
public class ActivityRepository implements IActivityRepository {

    @Resource
    protected IRedisService redisService;
    @Resource
    protected IRaffleActivitySkuDao activitySkuDao;
    @Resource
    protected IRaffleActivityDao activityDao;
    @Resource
    protected IRaffleActivityCountDao activityCountDao;
    @Resource
    protected IRaffleActivityOrderDao activityOrderDao;
    @Resource
    protected IUserRaffleOrderDao userRaffleOrderDao;
    @Resource
    protected IRaffleActivityAccountDao activityAccountDao;
    @Resource
    protected IRaffleActivityAccountMonthDao activityAccountMonthDao;
    @Resource
    protected IRaffleActivityAccountDayDao activityAccountDayDao;
    @Resource
    protected IUserCreditAccountDao creditAccountDao;


    @Resource
    protected IDBRouterStrategy dbRouter;
    @Resource
    private TransactionTemplate transactionTemplate;
    @Resource
    private ActivitySkuStockZeroMessageEvent activitySkuStockZeroMessageEvent;
    @Resource
    private EventPublisher eventPublisher;

    @Override
    public ActivitySkuEntity queryActivitySku(Long sku) {
        RaffleActivitySku raffleActivitySku = activitySkuDao.queryRaffleActivitySku(sku);
        return ActivitySkuEntity.builder()
                .sku(raffleActivitySku.getSku())
                .activityId(raffleActivitySku.getActivityId())
                .activityCountId(raffleActivitySku.getActivityCountId())
                .stockCount(raffleActivitySku.getStockCount())
                .stockCountSurplus(raffleActivitySku.getStockCountSurplus())
                .productAmount(raffleActivitySku.getProductAmount())
                .build();
    }

    @Override
    public ActivityEntity queryRaffleActivityByActivityId(Long activityId) {
        // 优先从缓存中获取
        String cacheKey = Constants.RedisKey.ACTIVITY_KEY + activityId;
        ActivityEntity activityEntity = redisService.getValue(cacheKey);
        if (null != activityEntity) return activityEntity;
        // 从dao层中获取
        RaffleActivity raffleActivity = activityDao.queryRaffleActivityByActivityId(activityId);
        activityEntity = ActivityEntity.builder()
                .activityId(raffleActivity.getActivityId())
                .activityName(raffleActivity.getActivityName())
                .activityDesc(raffleActivity.getActivityDesc())
                .beginDateTime(raffleActivity.getBeginDateTime())
                .endDateTime(raffleActivity.getEndDateTime())
                .strategyId(raffleActivity.getStrategyId())
                .state(raffleActivity.getState())
                .build();
        redisService.setValue(cacheKey, activityEntity);
        return activityEntity;
    }

    @Override
    public ActivityCountEntity queryActivityCountByActivityCountId(Long activityCountId) {
        // 优先从缓存中获取
        String cacheKey = Constants.RedisKey.ACTIVITY_COUNT_KEY + activityCountId;
        ActivityCountEntity activityCountEntity = redisService.getValue(cacheKey);
        if (null != activityCountEntity) return activityCountEntity;
        // 缓存中没有, 从dao层获取, 并保存到缓存中
        RaffleActivityCount raffleActivityCount =  activityCountDao.queryActivityCountByActivityCountId(activityCountId);
        activityCountEntity = ActivityCountEntity.builder()
                .activityCountId(raffleActivityCount.getActivityCountId())
                .totalCount(raffleActivityCount.getTotalCount())
                .dayCount(raffleActivityCount.getDayCount())
                .monthCount(raffleActivityCount.getMonthCount())
                .build();
        redisService.setValue(cacheKey, activityCountEntity);
        return activityCountEntity;

    }

    @Override
    public void cacheActivitySkuStock(String cacheKey, Integer stockCount) {
        boolean exists = redisService.isExists(cacheKey);
        if (exists) return;
        // redis中没有自己加载进缓存中
        redisService.setValue(cacheKey, stockCount);
    }

    @Override
    public boolean subtractionActivitySkuStock(Long sku, String cacheKey, Date endDateTime) {
        long surplus = redisService.decr(cacheKey);
        if (surplus == 0) {
            // 库存消耗完了, 发送MQ消息, 更新数据库缓存
            eventPublisher.publish(activitySkuStockZeroMessageEvent.topic(), activitySkuStockZeroMessageEvent.buildEventMessage(sku));
            return false;
        } else if (surplus < 0) {
            // 库存小于0 , 恢复为0个
            redisService.setValue(cacheKey, 0);
            return false;
        }

        // 用cacheKey decr后的值, 和key组成为库存锁key进行使用
        // 加锁是为了防呆设计, 能防止运营的错误恢复, 算是运营的防呆设计
        // 设置加锁时间为活动到期时间 + 延迟一天
        String localKey = cacheKey + Constants.UNDERLINE + surplus;
        long expireMillis = endDateTime.getTime() - System.currentTimeMillis() + TimeUnit.DAYS.toMillis(1);
        Boolean lock = redisService.setNx(localKey, expireMillis, TimeUnit.MILLISECONDS);
        if (!lock) {
            log.info("活动sku库存加锁失败 {}", localKey);
        }
        return lock;
    }

    @Override
    public void activitySkuStockConsumeSendQueue(ActivitySkuStockKeyVO activitySkuStockKeyVO) {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUERY_KEY;
        RBlockingQueue<ActivitySkuStockKeyVO> blockingQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<ActivitySkuStockKeyVO> delayedQueue = redisService.getDelayedQueue(blockingQueue);
        delayedQueue.offer(activitySkuStockKeyVO, 3, TimeUnit.SECONDS);
    }

    @Override
    public ActivitySkuStockKeyVO takeQueueValue() {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUERY_KEY;
        RBlockingQueue<ActivitySkuStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
        return destinationQueue.poll();
    }

    @Override
    public void clearQueueValue() {
        String cacheKey = Constants.RedisKey.ACTIVITY_SKU_COUNT_QUERY_KEY;
        RBlockingQueue<ActivitySkuStockKeyVO> destinationQueue = redisService.getBlockingQueue(cacheKey);
        RDelayedQueue<ActivitySkuStockKeyVO> delayedQueue = redisService.getDelayedQueue(destinationQueue);
        destinationQueue.clear();
        delayedQueue.clear();
    }

    @Override
    public void updateActivitySkuStock(Long sku) {
        activitySkuDao.updateActivitySkuStock(sku);
    }

    @Override
    public void clearActivitySkuStock(Long sku) {
        activitySkuDao.clearActivitySkuStock(sku);
    }

    @Override
    public UserRaffleOrderEntity queryNoUsedRaffleOrder(PartakeRaffleActivityEntity partakeRaffleActivityEntity) {
        // 查询到用户没有使用的订单
        UserRaffleOrder userRaffleOrderReq = new UserRaffleOrder();
        userRaffleOrderReq.setActivityId(partakeRaffleActivityEntity.getActivityId());
        userRaffleOrderReq.setUserId(partakeRaffleActivityEntity.getUserId());
        UserRaffleOrder userRaffleOrder = userRaffleOrderDao.queryNoUsedRaffleOrder(userRaffleOrderReq);

        if (null == userRaffleOrder) return null;
        return UserRaffleOrderEntity.builder()
                .userId(userRaffleOrder.getUserId())
                .activityId(userRaffleOrder.getActivityId())
                .activityName(userRaffleOrder.getActivityName())
                .strategyId(userRaffleOrder.getStrategyId())
                .orderId(userRaffleOrder.getOrderId())
                .orderTime(userRaffleOrder.getOrderTime())
                .orderState(userRaffleOrder.getOrderState())
                .build();
    }

    @Override
    public void saveCreatePartakeOrderAggregate(CreatePartakeOrderAggregate createPartakeOrderAggregate) {
        // 执行事务, 将订单和余额修改入库
        try {
            String userId = createPartakeOrderAggregate.getUserId();
            Long activityId = createPartakeOrderAggregate.getActivityId();
            ActivityAccountMonthEntity activityAccountMonthEntity = createPartakeOrderAggregate.getActivityAccountMonthEntity();
            ActivityAccountEntity activityAccountEntity = createPartakeOrderAggregate.getActivityAccountEntity();
            ActivityAccountDayEntity activityAccountDayEntity = createPartakeOrderAggregate.getActivityAccountDayEntity();
            UserRaffleOrderEntity userRaffleOrderEntity = createPartakeOrderAggregate.getUserRaffleOrderEntity();

            // 统一切换路由, 以下事务内的所有操作, 都走一个路由
            dbRouter.doRouter(userId);
            transactionTemplate.execute(status -> {
                try {
                    // 1. 更新账户
                    int totalCount = activityAccountDao.updateActivityAccountSubtractionQuota(
                            RaffleActivityAccount.builder()
                                    .activityId(activityId)
                                    .userId(userId)
                                    .build()
                    );
                    if (1 != totalCount) {
                        // 未更新成功, 回滚
                        status.setRollbackOnly();
                        log.warn("写入创建参与活动记录, 更新总账户额度不足, 异常 userId: {} activityId: {}", userId, activityId);
                        throw new AppException(ResponseCode.ACCOUNT_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_QUOTA_ERROR.getInfo());
                    }
                    // 2. 创建或更新月账户, true - 存在即更新, false - 不存在即创建
                    if (createPartakeOrderAggregate.isExistAccountMonth()){
                        // 存在月账户, 更新月账户
                        int monthCount = activityAccountMonthDao.updateActivityAccountMonthSubtractionQuota(
                                RaffleActivityAccountMonth.builder()
                                        .activityId(activityId)
                                        .userId(userId)
                                        .month(activityAccountMonthEntity.getMonth())
                                        .build()
                        );
                        if (1 != monthCount) {
                            // 更新失败, 回滚
                            status.setRollbackOnly();
                            log.warn("写入创建参与活动记录, 更新月账户额度不足, 异常 userId: {} activityId: {} month: {}", userId, activityId, activityAccountMonthEntity.getMonth());
                            throw new AppException(ResponseCode.ACCOUNT_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_QUOTA_ERROR.getInfo());
                        }
                    } else {
                        // 不存在月账户, 创建月账户
                        activityAccountMonthDao.insertActivityAccountMonth(RaffleActivityAccountMonth
                                .builder()
                                .month(activityAccountMonthEntity.getMonth())
                                .monthCount(activityAccountMonthEntity.getMonthCount())
                                .monthCountSurplus(activityAccountMonthEntity.getMonthCountSurplus() - 1)
                                .userId(userId)
                                .activityId(activityId)
                                .build()
                        );
                        // 新创建月账户, 则更新总账户中月镜像额度, 这里就相当于每个月的时候刷新用户的可用次数了
                        activityAccountDao.updateActivityAccountMonthSubtractionImageQuota(RaffleActivityAccount
                                .builder()
                                .userId(userId)
                                .activityId(activityId)
                                .monthCountSurplus(activityAccountEntity.getMonthCountSurplus())
                                .build());
                    }

                    // 3. 创建或更新日账户, true - 存在即更新, false - 不存在即创建
                    if (createPartakeOrderAggregate.isExistAccountDay()){
                        // 存在月账户, 更新月账户
                        int dayCount = activityAccountDayDao.updateActivityAccountDaySubtractionQuota(
                                RaffleActivityAccountDay.builder()
                                        .activityId(activityId)
                                        .userId(userId)
                                        .day(activityAccountDayEntity.getDay())
                                        .build()
                        );
                        if (1 != dayCount) {
                            // 更新失败, 回滚
                            status.setRollbackOnly();
                            log.warn("写入创建参与活动记录, 更新日账户额度不足, 异常 userId: {} activityId: {} day: {}", userId, activityId, activityAccountDayEntity.getDay());
                            throw new AppException(ResponseCode.ACCOUNT_QUOTA_ERROR.getCode(), ResponseCode.ACCOUNT_QUOTA_ERROR.getInfo());
                        }
                    } else {
                        // 不存在日账户, 创建日账户
                        activityAccountDayDao.insertActivityAccountDay(RaffleActivityAccountDay
                                .builder()
                                .day(activityAccountDayEntity.getDay())
                                .dayCount(activityAccountDayEntity.getDayCount())
                                .dayCountSurplus(activityAccountDayEntity.getDayCountSurplus() - 1)
                                .userId(userId)
                                .activityId(activityId)
                                .build()
                        );
                        // 新创建日账户, 则更新总账户中日镜像额度, 这里就相当于每天的时候刷新用户的可用次数了
                        activityAccountDao.updateActivityAccountDaySubtractionImageQuota(RaffleActivityAccount
                                .builder()
                                .userId(userId)
                                .activityId(activityId)
                                .dayCountSurplus(activityAccountDayEntity.getDayCountSurplus())
                                .build());
                    }

                    // 4.  写入参与活动订单
                    userRaffleOrderDao.insert(UserRaffleOrder.builder()
                            .userId(userRaffleOrderEntity.getUserId())
                            .activityId(userRaffleOrderEntity.getActivityId())
                            .activityName(userRaffleOrderEntity.getActivityName())
                            .orderId(userRaffleOrderEntity.getOrderId())
                            .orderTime(userRaffleOrderEntity.getOrderTime())
                            .orderState(userRaffleOrderEntity.getOrderState())
                            .strategyId(userRaffleOrderEntity.getStrategyId())
                            .build());

                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("写入创建参与活动记录，唯一索引冲突 userId: {} activityId: {}", userId, activityId, e);
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
        }
    }

    @Override
    public ActivityAccountEntity queryActivityAccountByUserId(String userId, Long activityId) {
        // 查询账户
        RaffleActivityAccount activityAccountReq = new RaffleActivityAccount();
        activityAccountReq.setUserId(userId);
        activityAccountReq.setActivityId(activityId);
        RaffleActivityAccount activityAccount = activityAccountDao.queryActivityAccountByUserId(activityAccountReq);
        if (null == activityAccount) return null;
        // 构建返回
        return ActivityAccountEntity.builder()
                .userId(activityAccount.getUserId())
                .activityId(activityAccount.getActivityId())
                .totalCount(activityAccount.getTotalCount())
                .totalCountSurplus(activityAccount.getTotalCountSurplus())
                .dayCount(activityAccount.getDayCount())
                .dayCountSurplus(activityAccount.getDayCountSurplus())
                .monthCount(activityAccount.getMonthCount())
                .monthCountSurplus(activityAccount.getMonthCountSurplus())
                .build();
    }

    @Override
    public ActivityAccountMonthEntity queryActivityAccountMonthByUserId(String userId, Long activityId, String month) {
        // 查询账户
        RaffleActivityAccountMonth activityAccountMonthReq = new RaffleActivityAccountMonth();
        activityAccountMonthReq.setActivityId(activityId);
        activityAccountMonthReq.setMonth(month);
        activityAccountMonthReq.setUserId(userId);
        RaffleActivityAccountMonth activityAccountMonth = activityAccountMonthDao.queryActivityAccountMonthByUserId(activityAccountMonthReq);
        if (null == activityAccountMonth) return null;
        // 构建返回
      return ActivityAccountMonthEntity.builder()
                .userId(activityAccountMonth.getUserId())
                .activityId(activityAccountMonth.getActivityId())
                .month(activityAccountMonth.getMonth())
                .monthCount(activityAccountMonth.getMonthCount())
                .monthCountSurplus(activityAccountMonth.getMonthCountSurplus())
                .build();
    }

    @Override
    public ActivityAccountDayEntity queryActivityAccountDayByUserId(String userId, Long activityId, String day) {
        // 查询账户
        RaffleActivityAccountDay activityAccountDayReq = new RaffleActivityAccountDay();
        activityAccountDayReq.setActivityId(activityId);
        activityAccountDayReq.setDay(day);
        activityAccountDayReq.setUserId(userId);
        RaffleActivityAccountDay activityAccountDay =  activityAccountDayDao.queryActivityAccountDayByUserId(activityAccountDayReq);
        if (null == activityAccountDay) return null;
        // 构建返回
        return ActivityAccountDayEntity.builder()
                .userId(activityAccountDay.getUserId())
                .activityId(activityAccountDay.getActivityId())
                .day(activityAccountDay.getDay())
                .dayCount(activityAccountDay.getDayCount())
                .dayCountSurplus(activityAccountDay.getDayCountSurplus())
                .build();
    }

    @Override
    public List<ActivitySkuEntity> queryActivitySkuListByActivityId(Long activityId) {
        List<RaffleActivitySku> activitySkus = activitySkuDao.queryActivitySkuListByActivityId(activityId);
        List<ActivitySkuEntity> activitySkuEntities = new ArrayList<>(activitySkus.size());
        for (RaffleActivitySku skus : activitySkus) {
            ActivitySkuEntity activitySkuEntity = ActivitySkuEntity.builder()
                        .sku(skus.getSku())
                        .activityId(skus.getActivityId())
                        .activityCountId(skus.getActivityCountId())
                        .stockCount(skus.getStockCount())
                        .stockCountSurplus(skus.getStockCountSurplus())
                        .build();
            activitySkuEntities.add(activitySkuEntity);
        }
        return activitySkuEntities;
    }

    @Override
    public Integer queryRaffleActivityAccountDayPartakeCount(Long activityId, String userId) {
        RaffleActivityAccountDay raffleActivityAccountDayReq = new RaffleActivityAccountDay();
        raffleActivityAccountDayReq.setDay(RaffleActivityAccountDay.currentDay());
        raffleActivityAccountDayReq.setActivityId(activityId);
        raffleActivityAccountDayReq.setUserId(userId);
        Integer dayPartakeCount = activityAccountDayDao.queryActivityAccountDayPartakeCount(raffleActivityAccountDayReq);
        return null == dayPartakeCount ? 0 : dayPartakeCount;
    }

    @Override
    public ActivityAccountEntity queryActivityAccountEntity(String userId, Long activityId) {
        // 1. 查询总账户额度
        RaffleActivityAccount raffleActivityAccount = activityAccountDao.queryActivityAccountByUserId(RaffleActivityAccount.builder()
                .activityId(activityId)
                .userId(userId).build());

        if (null == raffleActivityAccount) {
            return ActivityAccountEntity.builder()
                    .userId(userId)
                    .activityId(activityId)
                    .totalCount(0)
                    .totalCountSurplus(0)
                    .monthCountSurplus(0)
                    .monthCount(0)
                    .dayCountSurplus(0)
                    .dayCount(0)
                    .build();
        }

        // 2. 查询月账户额度
        RaffleActivityAccountMonth raffleActivityAccountMonth = activityAccountMonthDao.queryActivityAccountMonthByUserId(RaffleActivityAccountMonth.builder()
                .activityId(activityId)
                .userId(userId)
                .month(RaffleActivityAccountMonth.currentMonth())
                .build());

        // 3. 查询日账户额度
        RaffleActivityAccountDay raffleActivityAccountDay = activityAccountDayDao.queryActivityAccountDayByUserId(RaffleActivityAccountDay.builder()
                .activityId(activityId)
                .userId(userId)
                .day(RaffleActivityAccountDay.currentDay())
                .build());

        // 4. 组装返回对象
        ActivityAccountEntity activityAccountEntity = new ActivityAccountEntity();
        activityAccountEntity.setActivityId(activityId);
        activityAccountEntity.setUserId(userId);
        activityAccountEntity.setTotalCountSurplus(raffleActivityAccount.getTotalCountSurplus());
        activityAccountEntity.setTotalCount(raffleActivityAccount.getTotalCount());

        // 如果没有月账户, 则使用总账户中的月额度填充
        if (null == raffleActivityAccountMonth) {
            activityAccountEntity.setMonthCountSurplus(raffleActivityAccount.getMonthCountSurplus());
            activityAccountEntity.setMonthCount(raffleActivityAccount.getMonthCount());
        } else {
            activityAccountEntity.setMonthCount(raffleActivityAccountMonth.getMonthCount());
            activityAccountEntity.setMonthCountSurplus(raffleActivityAccountMonth.getMonthCountSurplus());
        }

        // 如果没有日账户, 则使用总账户中的日额度填充
        if (null == raffleActivityAccountDay) {
            activityAccountEntity.setDayCount(raffleActivityAccount.getDayCount());
            activityAccountEntity.setDayCountSurplus(raffleActivityAccount.getDayCountSurplus());
        } else {
            activityAccountEntity.setDayCountSurplus(raffleActivityAccountDay.getDayCountSurplus());
            activityAccountEntity.setDayCount(raffleActivityAccountDay.getDayCount());
        }

        return activityAccountEntity;
    }

    @Override
    public void updateCreditOrder(DeliveryOrderEntity deliveryOrderEntity) {
        RLock lock = redisService.getLock(Constants.RedisKey.ACTIVITY_ACCOUNT_UPDATE_LOCK + deliveryOrderEntity.getUserId());
        try {
            lock.lock(3, TimeUnit.SECONDS);

            // 查询订单
            RaffleActivityOrder raffleActivityOrderReq = new RaffleActivityOrder();
            raffleActivityOrderReq.setUserId(deliveryOrderEntity.getUserId());
            raffleActivityOrderReq.setOutBusinessNo(deliveryOrderEntity.getOutBusinessNo());
            RaffleActivityOrder raffleActivityOrderRes = activityOrderDao.queryRaffleActivityOrder(raffleActivityOrderReq);

            // 账户对象 - 总
            RaffleActivityAccount raffleActivityAccount = new RaffleActivityAccount();
            raffleActivityAccount.setUserId(raffleActivityOrderRes.getUserId());
            raffleActivityAccount.setActivityId(raffleActivityOrderRes.getActivityId());
            raffleActivityAccount.setTotalCount(raffleActivityOrderRes.getTotalCount());
            raffleActivityAccount.setTotalCountSurplus(raffleActivityOrderRes.getTotalCount());
            raffleActivityAccount.setDayCount(raffleActivityOrderRes.getDayCount());
            raffleActivityAccount.setDayCountSurplus(raffleActivityOrderRes.getDayCount());
            raffleActivityAccount.setMonthCount(raffleActivityOrderRes.getMonthCount());
            raffleActivityAccount.setMonthCountSurplus(raffleActivityOrderRes.getMonthCount());

            // 账户对象 - 月
            RaffleActivityAccountMonth raffleActivityAccountMonth = new RaffleActivityAccountMonth();
            raffleActivityAccountMonth.setUserId(raffleActivityOrderRes.getUserId());
            raffleActivityAccountMonth.setActivityId(raffleActivityOrderRes.getActivityId());
            raffleActivityAccountMonth.setMonth(RaffleActivityAccountMonth.currentMonth());
            raffleActivityAccountMonth.setMonthCount(raffleActivityOrderRes.getMonthCount());
            raffleActivityAccountMonth.setMonthCountSurplus(raffleActivityOrderRes.getMonthCount());

            // 账户对象 - 日
            RaffleActivityAccountDay raffleActivityAccountDay = new RaffleActivityAccountDay();
            raffleActivityAccountDay.setUserId(raffleActivityOrderRes.getUserId());
            raffleActivityAccountDay.setActivityId(raffleActivityOrderRes.getActivityId());
            raffleActivityAccountDay.setDay(RaffleActivityAccountDay.currentDay());
            raffleActivityAccountDay.setDayCount(raffleActivityOrderRes.getDayCount());
            raffleActivityAccountDay.setDayCountSurplus(raffleActivityOrderRes.getDayCount());

            dbRouter.doRouter(deliveryOrderEntity.getUserId());
            // 编程式事务
            transactionTemplate.execute(status -> {
                try {
                    // 1. 更新订单状态为已支付
                    int updateCount = activityOrderDao.updateOrderCompleted(raffleActivityOrderReq);
                    // 没有找到这个订单
                    if (1 != updateCount) {
                        status.setRollbackOnly();
                        return 1;
                    }
                    // 2. 更新账户 - 总
                    RaffleActivityAccount raffleActivityAccountRes = activityAccountDao.queryActivityAccountByUserId(raffleActivityAccount);
                    if (null == raffleActivityAccountRes) {
                        // 没有账户, 创建账户
                        activityAccountDao.insert(raffleActivityAccount);
                    } else {
                        // 有账户, 更新账户, 为账户添加额度
                        activityAccountDao.updateAccountQuota(raffleActivityAccount);
                    }
                    // 4. 更新账户 - 日
                    activityAccountDayDao.addAccountQuota(raffleActivityAccountDay);
                    // 5. 更新账户 - 月
                    activityAccountMonthDao.addAccountQuota(raffleActivityAccountMonth);
                    return 1;
                } catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("更新订单记录, 完成态, 唯一索引冲突 userId: {}, outBusinessNo: {}", deliveryOrderEntity.getUserId(), deliveryOrderEntity.getOutBusinessNo());
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
            lock.unlock();
        }
    }

    @Override
    public void doSaveCreditPayOrder(CreateQuotaOrderAggregate createQuotaOrderAggregate) {
        try {
            ActivityOrderEntity activityOrderEntity = createQuotaOrderAggregate.getActivityOrderEntity();
            RaffleActivityOrder activityOrder = new RaffleActivityOrder();
            activityOrder.setUserId(activityOrderEntity.getUserId());
            activityOrder.setSku(activityOrderEntity.getSku());
            activityOrder.setActivityId(activityOrderEntity.getActivityId());
            activityOrder.setActivityName(activityOrderEntity.getActivityName());
            activityOrder.setStrategyId(activityOrderEntity.getStrategyId());
            activityOrder.setOrderId(activityOrderEntity.getOrderId());
            activityOrder.setOrderTime(activityOrderEntity.getOrderTime());
            activityOrder.setTotalCount(createQuotaOrderAggregate.getTotalCount());
            activityOrder.setDayCount(createQuotaOrderAggregate.getDayCount());
            activityOrder.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            activityOrder.setPayAmount(activityOrderEntity.getPayAmount());
            activityOrder.setState(activityOrderEntity.getState().getCode());
            activityOrder.setOutBusinessNo(activityOrderEntity.getOutBusinessNo());
            // 以用户ID作为切分键, 通过 doRouter 设定路由 保证了接下来的操作都是在同一个连接下面, 保证了事务的特性
            dbRouter.doRouter(createQuotaOrderAggregate.getUserId());

            transactionTemplate.execute(status -> {
                try {
                    activityOrderDao.insert(activityOrder);
                    return 1;
                }catch (DuplicateKeyException e) {
                    status.setRollbackOnly();
                    log.error("写入订单记录, 唯一索引冲突 userId: {} activityId: {} sku: {}", activityOrder.getUserId(), activityOrder.getActivityId(), activityOrder.getSku());
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            dbRouter.clear();
        }
    }

    @Override
    public void doSaveNoPayOrder(CreateQuotaOrderAggregate createQuotaOrderAggregate) {
        RLock lock = redisService.getLock(Constants.RedisKey.ACTIVITY_ACCOUNT_LOCK + createQuotaOrderAggregate.getUserId() + Constants.UNDERLINE + createQuotaOrderAggregate.getActivityId());
        try {
            lock.lock(3, TimeUnit.SECONDS);

            // 订单对象
            ActivityOrderEntity activityOrderEntity = createQuotaOrderAggregate.getActivityOrderEntity();
            RaffleActivityOrder raffleActivityOrder = new RaffleActivityOrder();
            raffleActivityOrder.setUserId(activityOrderEntity.getUserId());
            raffleActivityOrder.setSku(activityOrderEntity.getSku());
            raffleActivityOrder.setActivityId(activityOrderEntity.getActivityId());
            raffleActivityOrder.setActivityName(activityOrderEntity.getActivityName());
            raffleActivityOrder.setStrategyId(activityOrderEntity.getStrategyId());
            raffleActivityOrder.setOrderId(activityOrderEntity.getOrderId());
            raffleActivityOrder.setOrderTime(activityOrderEntity.getOrderTime());
            raffleActivityOrder.setTotalCount(createQuotaOrderAggregate.getTotalCount());
            raffleActivityOrder.setDayCount(createQuotaOrderAggregate.getDayCount());
            raffleActivityOrder.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            raffleActivityOrder.setPayAmount(activityOrderEntity.getPayAmount());
            raffleActivityOrder.setState(activityOrderEntity.getState().getCode());
            raffleActivityOrder.setOutBusinessNo(activityOrderEntity.getOutBusinessNo());

            // 账户对象 - 总
            RaffleActivityAccount raffleActivityAccount = new RaffleActivityAccount();
            raffleActivityAccount.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccount.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccount.setTotalCount(createQuotaOrderAggregate.getTotalCount());
            raffleActivityAccount.setTotalCountSurplus(createQuotaOrderAggregate.getTotalCount());
            raffleActivityAccount.setDayCount(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccount.setDayCountSurplus(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccount.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            raffleActivityAccount.setMonthCountSurplus(createQuotaOrderAggregate.getMonthCount());

            // 账户对象 - 月
            RaffleActivityAccountMonth raffleActivityAccountMonth = new RaffleActivityAccountMonth();
            raffleActivityAccountMonth.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccountMonth.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccountMonth.setMonth(RaffleActivityAccountMonth.currentMonth());
            raffleActivityAccountMonth.setMonthCount(createQuotaOrderAggregate.getMonthCount());
            raffleActivityAccountMonth.setMonthCountSurplus(createQuotaOrderAggregate.getMonthCount());

            // 账户对象 - 日
            RaffleActivityAccountDay raffleActivityAccountDay = new RaffleActivityAccountDay();
            raffleActivityAccountDay.setUserId(createQuotaOrderAggregate.getUserId());
            raffleActivityAccountDay.setActivityId(createQuotaOrderAggregate.getActivityId());
            raffleActivityAccountDay.setDay(RaffleActivityAccountDay.currentDay());
            raffleActivityAccountDay.setDayCount(createQuotaOrderAggregate.getDayCount());
            raffleActivityAccountDay.setDayCountSurplus(createQuotaOrderAggregate.getDayCount());

            // 以用户ID作为切分键, 通过 doRouter 设定路由 保证了接下来的操作都是在同一个连接下面, 保证了事务的特性
            dbRouter.doRouter(createQuotaOrderAggregate.getUserId());
            transactionTemplate.execute(status -> {
                try {
                    // 1. 写入订单
                    activityOrderDao.insert(raffleActivityOrder);
                    // 2. 更新账户 - 总
                    RaffleActivityAccount raffleActivityAccountRes = activityAccountDao.queryActivityAccountByUserId(raffleActivityAccount);
                    if (null == raffleActivityAccountRes) {
                        activityAccountDao.insert(raffleActivityAccount);
                    } else {
                        activityAccountDao.updateAccountQuota(raffleActivityAccount);
                    }
                    // 3. 更新账户 - 月
                    activityAccountMonthDao.addAccountQuota(raffleActivityAccountMonth);
                    // 4. 更新账户 - 日
                    activityAccountDayDao.addAccountQuota(raffleActivityAccountDay);
                    return 1;
                } catch (DuplicateKeyException e) {
                    log.error("写入订单记录, 唯一索引冲突 userId: {} activityId: {} sku: {}", activityOrderEntity.getUserId(), activityOrderEntity.getActivityId(), activityOrderEntity.getSku());
                    status.setRollbackOnly();
                    throw new AppException(ResponseCode.INDEX_DUP.getCode(), e);
                }
            });
        } finally {
            lock.unlock();
            dbRouter.clear();
        }
    }

    @Override
    public UnpaidActivityOrderEntity queryUnpaidActivityOrder(SkuRechargeEntity skuRechargeEntity) {
        RaffleActivityOrder raffleActivityOrderReq = new RaffleActivityOrder();
        raffleActivityOrderReq.setUserId(skuRechargeEntity.getUserId());
        raffleActivityOrderReq.setSku(skuRechargeEntity.getSku());
        RaffleActivityOrder raffleActivityOrderRes = activityOrderDao.queryUnpaidActivityOrder(raffleActivityOrderReq);
        if (null == raffleActivityOrderRes) return null;
        return UnpaidActivityOrderEntity.builder()
                .userId(raffleActivityOrderReq.getUserId())
                .orderId(raffleActivityOrderRes.getOrderId())
                .outBusinessNo(raffleActivityOrderRes.getOutBusinessNo())
                .payAmount(raffleActivityOrderRes.getPayAmount())
                .build();
    }

    @Override
    public List<SkuProductEntity> querySkuProductEntityListByActivityId(Long activityId) {
        List<RaffleActivitySku> activitySkus = activitySkuDao.queryActivitySkuListByActivityId(activityId);
        List<SkuProductEntity> skuProductEntities = new ArrayList<>(activitySkus.size());
        for (RaffleActivitySku sku : activitySkus) {

            RaffleActivityCount raffleActivityCount = activityCountDao.queryActivityCountByActivityCountId(sku.getActivityCountId());
            SkuProductEntity.ActivityCount activityCount = new SkuProductEntity.ActivityCount();
             activityCount.setTotalCount(raffleActivityCount.getTotalCount());
             activityCount.setDayCount(raffleActivityCount.getDayCount());
             activityCount.setMonthCount(raffleActivityCount.getMonthCount());

            SkuProductEntity skuProductEntity = SkuProductEntity.builder()
                          .sku(sku.getSku())
                          .activityId(sku.getActivityId())
                          .activityCountId(sku.getActivityCountId())
                          .stockCount(sku.getStockCount())
                          .stockCountSurplus(sku.getStockCountSurplus())
                          .productAmount(sku.getProductAmount())
                          .activityCount(activityCount)
                          .build();

            skuProductEntities.add(skuProductEntity);
        }
        return skuProductEntities;
    }

    @Override
    public BigDecimal queryUserCreditAccountAmount(String userId) {
        try {
            dbRouter.doRouter(userId);
            UserCreditAccount userCreditAccountReq = new UserCreditAccount();
            userCreditAccountReq.setUserId(userId);
            UserCreditAccount userCreditAccount = creditAccountDao.queryUserCreditAccount(userCreditAccountReq);
            if (null == userCreditAccount) return BigDecimal.ZERO;
            return userCreditAccount.getAvailableAmount();
        } finally {
            dbRouter.clear();
        }
    }

}
